//
// Copyright (C) 2020-2022 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

package redis

import (
	"fmt"
	"github.com/edgexfoundry/edgex-go/internal/app/models"
	"github.com/edgexfoundry/edgex-go/internal/app/pkg/controller/http"
	"github.com/edgexfoundry/edgex-go/internal/app/pkg/utils"
	pkgCommon "github.com/edgexfoundry/edgex-go/internal/pkg/common"
	redisClient "github.com/edgexfoundry/edgex-go/internal/pkg/infrastructure/redis"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
	"github.com/gomodule/redigo/redis"
	"github.com/google/uuid"
)

type AiotClient redisClient.Client

func checkDeviceExist(conn redis.Conn, id string) errors.EdgeX {
	exists, err := deviceIdExists(conn, id)
	if err != nil {
		return errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return nil
}

// DeviceIdExists checks the device existence by id
func (c *AiotClient) DeviceIdExists(id string) (bool, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, id)
	if err != nil {
		return exists, errors.NewCommonEdgeX(errors.Kind(err), fmt.Sprintf("fail to check the device existence by id %s", id), err)
	}
	return exists, nil
}

// DeviceProfileAdd device profile add
func (c *AiotClient) DeviceProfileAdd(deviceId string, deviceProfile models.DeviceProfile) (d string, err errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return d, errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return d, errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}

	return deviceProfileAdd(conn, deviceId, deviceProfile)
}

// DeviceProfileById gets a device profile by id
func (c *AiotClient) DeviceProfileById(id string) (deviceProfile models.DeviceProfile, err errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	deviceProfile, err = deviceProfileById(conn, id)
	if err != nil {
		return deviceProfile, errors.NewCommonEdgeXWrapper(err)
	}

	return
}

// DeviceResourceAdd device resource add
func (c *AiotClient) DeviceResourceAdd(deviceId string, d models.DeviceResource) (string, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return "", errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return "", errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return deviceResourceAdd(conn, deviceId, d)
}

// DeviceResourceDelete device resource add
func (c *AiotClient) DeviceResourceDelete(deviceId, resourceName string) (string, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	return deviceResourceDelete(conn, deviceId, resourceName)
}

// DeviceResourceUpdate device resource update
func (c *AiotClient) DeviceResourceUpdate(deviceId string, dr models.DeviceResource) (string, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	return deviceResourceUpdate(conn, deviceId, dr)
}

// DeviceResourcesGetByDeviceId device resource find by device id
func (c *AiotClient) DeviceResourcesGetByDeviceId(deviceId string, offset, limit int) ([]models.DeviceResource, uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()
	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return nil, 0, errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return nil, 0, errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}

	deviceResourceKey := CreateKey(DeviceProfileResourceCollection, deviceId)
	count, edgeXerr := getMemberNumber(conn, ZCARD, deviceResourceKey)
	if edgeXerr != nil {
		return nil, 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	devices, err := deviceResourcesGetByDeviceId(conn, deviceId, offset, limit)
	return devices, count, err
}

// DeviceCommandAdd device resource add
func (c *AiotClient) DeviceCommandAdd(deviceId string, d models.DeviceCommand) (string, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return "", errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return "", errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return deviceCommandAdd(conn, deviceId, d)
}

// DeviceCommandGet device command query
func (c *AiotClient) DeviceCommandGet(deviceId string, offset, limit int) ([]models.DeviceCommand, uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return nil, 0, errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return nil, 0, errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}

	deviceCommandKey := CreateKey(DeviceProfileCommandCollection, deviceId)
	count, edgeXerr := getMemberNumber(conn, ZCARD, deviceCommandKey)
	if edgeXerr != nil {
		return nil, 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	devices, err := deviceCommandGet(conn, deviceId, offset, limit)
	return devices, count, err
}

// DeviceCommandDelete device command delete
func (c *AiotClient) DeviceCommandDelete(deviceId, commandId string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return deviceCommandDelete(conn, deviceId, commandId)
}

// DeviceCommandUpdate device command update
func (c *AiotClient) DeviceCommandUpdate(deviceId string, d models.DeviceCommand) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return deviceCommandUpdate(conn, deviceId, d)
}

// DevicesGetByLabels device get by labels
func (c *AiotClient) DevicesGetByLabels(labels []string, offset, limit int) ([]models.Device, uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	return devicesGetByLabels(conn, labels, offset, limit)
}

// DevicesGetByName device get by name
func (c *AiotClient) DevicesGetByName(name, productId string, offset, limit int) ([]models.Device, uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	return devicesGetByNameAndProduct(conn, name, productId, offset, limit)
}

// DevicesGetByProduct device get by product id
func (c *AiotClient) DevicesGetByProduct(productId string, offset, limit int) ([]models.Device, uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	return devicesGetByProduct(conn, productId, offset, limit)
}

// DeviceAdd device add
func (c *AiotClient) DeviceAdd(device models.Device) (d models.Device, err errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	client := (*redisClient.Client)(c)
	exist, err := client.DeviceServiceNameExists(device.ServiceName)
	if err != nil {
		return d, errors.NewCommonEdgeXWrapper(err)
	}
	if !exist {
		return d, errors.NewCommonEdgeX(errors.KindServerError, fmt.Sprintf(http.DeviceServiceNotExist, device.ServiceName), nil)
	}
	return deviceAdd(conn, device)
}

// DeviceGetById device get by id
func (c *AiotClient) DeviceGetById(deviceId string) (models.Device, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, deviceId)
	if err != nil {
		return models.Device{}, errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return models.Device{}, errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return deviceById(conn, deviceId)
}

// DeviceUpdate device update
func (c *AiotClient) DeviceUpdate(device models.Device) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := deviceIdExists(conn, device.Id)
	if err != nil {
		return errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
	} else if !exists {
		return errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
	}
	return deviceUpdate(conn, device)
}

// DeviceDelete device delete
func (c *AiotClient) DeviceDelete(deviceIds ...string) ([]models.Device, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	for _, deviceId := range deviceIds {
		exists, err := deviceIdExists(conn, deviceId)
		if err != nil {
			return nil, errors.NewCommonEdgeX(errors.Kind(err), http.DeviceExistCheckFailed, err)
		} else if !exists {
			return nil, errors.NewCommonEdgeX(errors.KindInvalidId, http.DeviceNotExist, nil)
		}
	}

	return deviceDelete(conn, deviceIds...)
}

func (c *AiotClient) AutoEventAdd(deviceId string, d models.AutoEvent) (string, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return "", err
	}

	id, _ := utils.GenerateUUID()
	d.Id = id
	return id, autoEventAdd(conn, deviceId, d)
}

// AutoEventGetByDeviceId 获取设备所有自动采集事件
func (c *AiotClient) AutoEventGetByDeviceId(deviceId string) ([]models.AutoEvent, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return nil, err
	}

	return autoEventsGetByDeviceId(conn, deviceId)
}

// AutoEventDelete 删除自动采集
func (c *AiotClient) AutoEventDelete(deviceId, eventId string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return err
	}

	return deleteAllAutoEventByDeviceId(conn, deviceId)
}

// AutoEventUpdate 自动采集更新
func (c *AiotClient) AutoEventUpdate(deviceId string, d models.AutoEvent) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return err
	}

	exist, err := autoEventExist(conn, d.Id)
	if err != nil {
		return errors.NewCommonEdgeX(errors.KindServerError, http.DataBaseQueryError, err)
	} else if !exist {
		return errors.NewCommonEdgeX(errors.KindServerError, http.AutoEventNotExist, err)
	}

	return autoEventUpdate(conn, deviceId, d)
}

// TransportAdd 传输配置新增
func (c *AiotClient) TransportAdd(deviceId string, d models.Transport) (string, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return "", err
	}

	id, _ := utils.GenerateUUID()
	d.Id = id
	return id, transportAdd(conn, deviceId, d)
}

// TransportGetByDeviceId 获取设备所有传输配置
func (c *AiotClient) TransportGetByDeviceId(deviceId string, offset, limit int) ([]models.Transport, uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return nil, 0, err
	}

	transportKey := CreateKey(TransportDeviceCollection, deviceId)
	count, edgeXerr := getMemberNumber(conn, ZCARD, transportKey)
	if edgeXerr != nil {
		return nil, 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	transports, err := transportsGetByDeviceId(conn, deviceId, offset, limit)
	return transports, count, err
}

// TransportDelete 删除传输配置
func (c *AiotClient) TransportDelete(deviceId, transportId string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return err
	}

	return transportDelete(conn, deviceId, transportId)
}

// TransportUpdate 传输配置更新
func (c *AiotClient) TransportUpdate(deviceId string, d models.Transport) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return err
	}

	exist, err := transportExist(conn, d.Id)
	if err != nil {
		return errors.NewCommonEdgeX(errors.KindServerError, http.DataBaseQueryError, err)
	} else if !exist {
		return errors.NewCommonEdgeX(errors.KindEntityDoesNotExist, http.TransportNotExist, err)
	}

	return transportUpdate(conn, deviceId, d)
}

// SyncDeviceProfileById 同步设备元信息
func (c *AiotClient) SyncDeviceProfileById(deviceId string) (d models.DeviceProfile, err errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	err = checkDeviceExist(conn, deviceId)
	if err != nil {
		return
	}

	return syncDeviceProfileById(conn, deviceId)
}

// SyncAutoEventsToDevice 同步设备自动采集事件
func (c *AiotClient) SyncAutoEventsToDevice(deviceId string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	err := checkDeviceExist(conn, deviceId)
	if err != nil {
		return err
	}

	return syncAutoEventsToDevice(conn, deviceId)
}

// CountByDeviceStatus 设备数量统计
func (c *AiotClient) CountByDeviceStatus(duration int32) (models.DeviceCount, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	return countByDeviceStatus(conn, duration)
}

/**
 * @Description 产品分类查询
 * @method AllproductCategorys
 * @auth kely
 * @Date 2023-02-20
 * @Param
 * @return
 **/

// AllproductCategorys query the AllproductCategorys with offset, limit, and labels
func (c *AiotClient) AllproductCategorys(offset int, limit int, labels []string) ([]models.ProductCategory, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	productCategorys, edgeXerr := ProductCategorysByLabels(conn, offset, limit, labels)
	if edgeXerr != nil {
		return productCategorys, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return productCategorys, nil
}

/**
 * @Description 产品分类模糊查询
 * @method
 * @auth kely
 * @Date 2023-2-23
 * @Param
 * @return
 **/

func (c *AiotClient) ProductCategorysByName(offset int, limit int, labels string) ([]models.ProductCategory, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	productCategorys, edgeXerr := ProductCategorysByName(conn, offset, limit, labels)
	if edgeXerr != nil {
		return productCategorys, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return productCategorys, nil
}

/**
 * @Description 产品分组查询-不分页
 * @method
 * @auth kely
 * @Date 2023-3-8
 * @Param
 * @return
 **/

func (c *AiotClient) ProdcutCategoryOwn() ([]models.ProductCategory, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()
	productCategorys, edgeXerr := ProdcutCategoryOwn(conn)
	if edgeXerr != nil {
		return productCategorys, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return productCategorys, nil
}

/**
 * @Description 产品分组新增
 * @method
 * @auth kely
 * @Date 2023-2-22
 * @Param
 * @return
 **/

func (c *AiotClient) AddProductCategory(d models.ProductCategory) (models.ProductCategory, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	if len(d.Id) == 0 {
		d.Id = uuid.New().String()
	}

	return addProductCategory(conn, d)
}

/**
 * @Description 获取产品分组---通过id
 * @method
 * @auth kely
 * @Date 2023-2-28
 * @Param
 * @return
 **/

func (c *AiotClient) ProductCategoryById(id string) (productCategory models.ProductCategory, edgeXerr errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	productCategory, edgeXerr = productCategoryById(conn, id)
	if edgeXerr != nil {
		return productCategory, errors.NewCommonEdgeX(errors.Kind(edgeXerr), fmt.Sprintf("fail to query device by id %s", id), edgeXerr)
	}

	return
}

/**
 * @Description 更新数据-产品分组
 * @method
 * @auth kely
 * @Date 2023-2-28
 * @Param
 * @return
 **/

func (c *AiotClient) UpdateDevice(oldProductName string, category models.ProductCategory) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return updateProductCategory(conn, oldProductName, category)
}

// ProductCategory deletes a ProductCategory by id
func (c *AiotClient) DeleteProductCategoryById(category models.ProductCategory, s string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()
	storedKey := productCategoryStoredKey(category.Id)
	edgeXerr := deleteproductCategoryById(conn, storedKey, category)
	if edgeXerr != nil {
		return errors.NewCommonEdgeX(errors.Kind(edgeXerr), fmt.Sprintf("fail to delete the device with id %s", category.Name), edgeXerr)
	}

	return nil
}

/**
 * @Description 产品分组获取-通过id
 * @method
 * @auth kely
 * @Date 2023-2-28
 * @Param
 * @return
 **/

func productCategoryById(conn redis.Conn, id string) (productCategory models.ProductCategory, edgeXerr errors.EdgeX) {
	edgeXerr = getObjectById(conn, productCategoryStoredKey(id), &productCategory)
	if edgeXerr != nil {
		return productCategory, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return
}

/**
 * @Description 产品分组新增到DB
 * @method
 * @auth kely
 * @Date 2023-2-22
 * @Param
 * @return
 **/

func addProductCategory(conn redis.Conn, d models.ProductCategory) (models.ProductCategory, errors.EdgeX) {
	exists, edgeXerr := productCategoryIdExists(conn, d.Id)
	if edgeXerr != nil {
		return d, errors.NewCommonEdgeXWrapper(edgeXerr)
	} else if exists {
		return d, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("device id %s already exists", d.Id), edgeXerr)
	}

	exists, edgeXerr = productCategoryNameExists(conn, d.Name)
	if edgeXerr != nil {
		return d, errors.NewCommonEdgeXWrapper(edgeXerr)
	} else if exists {
		return d, errors.NewCommonEdgeX(errors.KindDuplicateName, fmt.Sprintf("device name %s already exists", d.Name), edgeXerr)
	}

	ts := pkgCommon.MakeTimestamp()
	if d.Created == 0 {
		d.Created = ts
	}
	d.Modified = ts

	storedKey := productCategoryStoredKey(d.Id)
	_ = conn.Send(MULTI)
	edgeXerr = sendAddProductCategoryCmd(conn, storedKey, d)
	if edgeXerr != nil {
		return d, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	_, err := conn.Do(EXEC)
	if err != nil {
		edgeXerr = errors.NewCommonEdgeX(errors.KindDatabaseError, "productCategory creation failed", err)
	}

	return d, edgeXerr
}

// Add a new product
func (c *AiotClient) AddProduct(p models.Product) (models.Product, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	if len(p.Id) == 0 {
		p.Id = uuid.New().String()
	}

	return addProduct(conn, p)
}

// AllProducts query the products with offset, limit
func (c *AiotClient) AllProducts(offset int, limit int, groupId string) ([]models.Product, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	products, edgeXerr := allProducts(conn, offset, limit, groupId)
	if edgeXerr != nil {
		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return products, nil
}

/**
 * @Description 产品查询-通过是否为可见
 * @method
 * @auth kely
 * @Date ${DATE}
 * @Param
 * @return
 **/

func (c *AiotClient) OwnProducts() ([]models.Product, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	products, edgeXerr := OwnProducts(conn)
	if edgeXerr != nil {
		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return products, nil
}

// AllProductCount returns the total count of Product.
func (c *AiotClient) AllProductCount(groupId string) (count uint32, edgeXerr errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()
	if groupId != "" {
		count, edgeXerr = getMemberNumber(conn, ZCARD, CreateKey(ProductCollectionGroup, groupId))
	} else {
		count, edgeXerr = getMemberNumber(conn, ZCARD, ProductCollection)
	}
	if edgeXerr != nil {
		return 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	return count, nil
}

// 通过名称查询产品
//func (c *AiotClient) ProductByName(name string) (product models.Product, edgeXerr errors.EdgeX) {
//	conn := c.Pool.Get()
//	defer conn.Close()
//
//	product, edgeXerr = productByName(conn, name)
//	if edgeXerr != nil {
//		return product, errors.NewCommonEdgeX(errors.Kind(edgeXerr), fmt.Sprintf("fail to query product by name %s", name), edgeXerr)
//	}
//	return
//}

// 通过名称模糊查询产品
func (c *AiotClient) ProductsByName(offset int, limit int, name string, groupId string) (products []models.Product, totalCount uint32, edgeXerr errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	products, totalCount, edgeXerr = productsByName(conn, offset, limit, name, groupId)
	if edgeXerr != nil {
		return products, totalCount, errors.NewCommonEdgeX(errors.Kind(edgeXerr), fmt.Sprintf("fail to query product by name %s", name), edgeXerr)
	}
	return products, totalCount, nil
}

// 产品id是否存在
func (c *AiotClient) ProductIdExists(id string) (bool, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	exists, err := productIdExists(conn, id)
	if err != nil {
		return exists, errors.NewCommonEdgeX(errors.Kind(err), fmt.Sprintf("fail to check the product existence by id %s", id), err)
	}
	return exists, nil
}

// productById 通过产品id查询产品
func (c *AiotClient) ProductById(id string) (product models.Product, edgeXerr errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	product, edgeXerr = productById(conn, id)
	if edgeXerr != nil {
		return product, errors.NewCommonEdgeX(errors.Kind(edgeXerr), fmt.Sprintf("fail to query product by id %s", id), edgeXerr)
	}
	return
}

// ProductDelete product delete
func (c *AiotClient) ProductDelete(productId string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return productDelete(conn, productId)
}

// ProductUpdate product update
func (c *AiotClient) ProductUpdate(p models.Product) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return ProductUpdate(conn, p)
}

// ProductsByGroupId query the products with groupId,offset, limit
//func (c *AiotClient) ProductsByGroupId(groupId string, offset int, limit int) ([]models.Product, errors.EdgeX) {
//	conn := c.Pool.Get()
//	defer conn.Close()
//
//	products, edgeXerr := ProductsByGroupId(conn, groupId, offset, limit)
//	if edgeXerr != nil {
//		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
//	}
//	return products, nil
//}

// ProductCountByGroupId 查询某个产品分类的产品数量
func (c *AiotClient) ProductCountByGroupId(groupId string) (uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	count, edgeXerr := getMemberNumber(conn, ZCARD, CreateKey(ProductCollectionGroup, groupId))
	if edgeXerr != nil {
		return 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	return count, nil
}

// AllProductsWithDeviceNum query the productswithdevicenum with offset, limit
//func (c *AiotClient) AllProductsWithDeviceNum(offset int, limit int) ([]models.Product, errors.EdgeX) {
//	conn := c.Pool.Get()
//	defer conn.Close()
//
//	products, edgeXerr := allProductsWithDEviceNum(conn, offset, limit)
//	if edgeXerr != nil {
//		return products, errors.NewCommonEdgeXWrapper(edgeXerr)
//	}
//	return products, nil
//}

// Add a new product attribute
func (c *AiotClient) AddProductAttribute(pa models.ProductAttribute) (models.ProductAttribute, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	if len(pa.Id) == 0 {
		pa.Id = uuid.New().String()
	}

	return addProductAttribute(conn, pa)
}

//func (c *AiotClient) ProductAttributeById(productAttributeId string) (models.ProductAttribute, errors.EdgeX) {
//	conn := c.Pool.Get()
//	defer conn.Close()
//
//	return ProductAttributeById(conn, productAttributeId)
//}

// 通过产品id查询产品的属性
func (c *AiotClient) AttributesByProductId(offset int, limit int, productId string) ([]models.ProductAttribute, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	productAttributes, edgeXerr := AttributesByProductId(conn, offset, limit, productId)
	if edgeXerr != nil {
		return productAttributes, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return productAttributes, nil
}

func (c *AiotClient) AttributeCountByProductId(productId string) (uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	count, edgeXerr := getMemberNumber(conn, ZCARD, CreateKey(ProductAttributeCollectionProductId, productId))
	if edgeXerr != nil {
		return 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	return count, nil
}

// ProductAttributeDelete product attribute delete
func (c *AiotClient) ProductAttributeDelete(productAttributeIds []string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return ProductAttributeDelete(conn, productAttributeIds)
}

// UpdateProductAttribute product attribute update
func (c *AiotClient) UpdateProductAttribute(pa models.ProductAttribute) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return UpdateProductAttribute(conn, pa)
}

// Add a new product function
func (c *AiotClient) AddProductFunction(pf models.ProductFunction) (models.ProductFunction, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	if len(pf.Id) == 0 {
		pf.Id = uuid.New().String()
	}

	return addProductFunction(conn, pf)
}

// 通过产品id查询产品功能
func (c *AiotClient) AllFunctionsByProductId(offset int, limit int, productId string) ([]models.ProductFunction, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	productFunctions, edgeXerr := AllFunctionsByProductId(conn, offset, limit, productId)
	if edgeXerr != nil {
		return productFunctions, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return productFunctions, nil
}

// AllFunctionCountByProductId returns the total count of ProductFunction.
func (c *AiotClient) AllFunctionCountByProductId(productId string) (uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	count, edgeXerr := getMemberNumber(conn, ZCARD, CreateKey(ProductFunctionCollectionProductId, productId))
	if edgeXerr != nil {
		return 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	return count, nil
}

// ProductFunctionDelete product function delete
func (c *AiotClient) ProductFunctionDelete(productFunctionIds []string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return ProductFunctionDelete(conn, productFunctionIds)
}

// UpdateProductFunction product function update
func (c *AiotClient) UpdateProductFunction(pf models.ProductFunction) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return UpdateProductFunction(conn, pf)
}

// Add a new product transport
func (c *AiotClient) AddProductTransport(pt models.ProductTransport) (models.ProductTransport, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	if len(pt.Id) == 0 {
		pt.Id = uuid.New().String()
	}

	return addProductTransport(conn, pt)
}

// 通过产品id查询产品传输配置
func (c *AiotClient) AllTransportsByProductId(offset int, limit int, productId string) ([]models.ProductTransport, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	productTransports, edgeXerr := AllTransportsByProductId(conn, offset, limit, productId)
	if edgeXerr != nil {
		return productTransports, errors.NewCommonEdgeXWrapper(edgeXerr)
	}
	return productTransports, nil
}

// AllTransportCountByProductId 统计该产品传输配置数量
func (c *AiotClient) AllTransportCountByProductId(productId string) (uint32, errors.EdgeX) {
	conn := c.Pool.Get()
	defer conn.Close()

	count, edgeXerr := getMemberNumber(conn, ZCARD, CreateKey(ProductTransportCollectionProductId, productId))
	if edgeXerr != nil {
		return 0, errors.NewCommonEdgeXWrapper(edgeXerr)
	}

	return count, nil
}

// ProductTransportDelete product transport delete
func (c *AiotClient) ProductTransportDelete(productTransportIds []string) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return ProductTransportDelete(conn, productTransportIds)
}

// UpdateProductTransport product transport update
func (c *AiotClient) UpdateProductTransport(pt models.ProductTransport) errors.EdgeX {
	conn := c.Pool.Get()
	defer conn.Close()

	return UpdateProductTransport(conn, pt)
}
